<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="../../aosa.css" type="text/css">
    <title>The Performance of Open Source Software: High Performance Networking in Chrome</title>
  </head>
  <body>

    <div class="titlebox">
      <h1>The Performance of Open Source Applications<br>High Performance Networking in Chrome</h1>
      <p class="author">Ilya Grigorik</p>
    </div>

<h2 id="history-and-guiding-principles-of-google-chrome">History and Guiding Principles of Google Chrome</h2>

<p>Google Chrome was first released in the second half of 2008, as a beta version for the Windows platform. The Google-authored code powering Chrome was also made available under a permissive <span class="caps">BSD</span> license–also known as the Chromium project. To many observers, this turn of events came as a surprise: the return of the browser wars? Could Google really do much better?</p>

<blockquote>
<p><span class="dquo">"</span>It was so good that it essentially forced me to change my mind…" - Eric Schmidt, on his <a href="http://blogs.wsj.com/digits/2009/07/09/sun-valley-schmidt-didnt-want-to-build-chrome-initially-he-says/">initial resistance</a> to the idea of developing Google Chrome.</p>
</blockquote>

<p>Turns out, they could. Today Chrome is one of the most widely used browsers on the web (<a href="http://gs.statcounter.com/?PHPSESSID=oc1i9oue7por39rmhqq2eouoh0">35%+</a> of the market share according to StatCounter) and is now available on Windows, Linux, <span class="caps">OS</span> X, Chrome <span class="caps">OS</span>, as well as Android and iOS platforms. Clearly, the features and the functionality resonated with the users, and many innovations of Chrome have also found their way into other popular browsers.</p>

<p>The original <a href="http://www.google.com/googlebooks/chrome/">38-page comic book</a> explanation of the ideas and innovations of Google Chrome offers a great overview of the thinking and design process behind the popular browser. However, this was only the beginning. The core principles that motivated the original development of the browser continue to be the guiding principles for ongoing improvements in Chrome:</p>

<dl>
<dt>Speed</dt>
<dd>Make the <strong>fastest</strong> browser
</dd>
<dt>Security</dt>
<dd>Provide the <strong>most secure</strong> environment to the user
</dd>
<dt>Stability</dt>
<dd>Provide a <strong>resilient and stable</strong> web application platform
</dd>
<dt>Simplicity</dt>
<dd>Create sophisticated technology, wrapped in a <strong>simple user experience</strong>
</dd>
</dl>

<p>As the team observed, many of the sites we use today are not just web pages, they are applications. In turn, the ever more ambitious applications require speed, security, and stability. Each of these deserves its own dedicated chapter, but since our subject is performance, our focus will be primarily on speed.</p>

<h2 id="the-many-facets-of-performance">The Many Facets of Performance</h2>

<p>A modern browser is a platform, just like your operating system, and Google Chrome is designed as such. Prior to Google Chrome, all major browsers were built as monolithic, single process applications. All open pages shared the same address space and contended for the same resources. A bug in any page, or the browser, ran the risk of compromising the entire experience.</p>

<p>By contrast, Chrome works on a multi-process model, which provides process and memory isolation, and a tight <a href="http://dev.chromium.org/developers/design-documents/sandbox">security sandbox</a> for each tab. In an increasingly multi-core world, the ability to isolate the processes as well as shield each open tab from other misbehaving pages alone proves that Chrome has a significant performance edge over the competition. In fact, it is important to note that most other browsers have followed suit, or are in the process of migrating to similar architecture.</p>

<p>With an allocated process in place, the execution of a web program primarily involves three tasks: fetching resources, page layout and rendering, and JavaScript execution. The rendering and script steps follow a single-threaded, interleaved model of execution–it is not possible to perform concurrent modifications of the resulting Document Object Model (<span class="caps">DOM</span>). This is in part due to the fact that JavaScript itself is a single-threaded language. Hence, optimizing how the rendering and script execution runtimes work together is of critical importance, both to the web developers building the applications as well as the developers working on the browser.</p>

<p>For rendering, Chrome uses Blink, which is a fast, open source, and standards compliant layout engine. For JavaScript, Chrome ships with its own, heavily optimized V8 JavaScript runtime, which was also released as a standalone open source project and has found its way into many other popular projects–e.g., runtime for Node.js. However, optimizing V8 JavaScript execution, or the Blink parsing and rendering pipelines will not do much good if the browser is blocked on the network, waiting for the resources to arrive.</p>

<p>The ability of the browser to optimize the order, priority, and latency of each network resource is one of the most critical contributors to the overall user experience. You may not be aware of it, but Chrome's network stack is, quite literally, getting smarter every day, trying to hide or decrease the latency cost of each resource: it learns likely <span class="caps">DNS</span> lookups, it remembers the topology of the web, it pre-connects to likely destination targets, and more. From the outside, it presents itself as a simple resource fetching mechanism, but from the inside it is an elaborate and a fascinating case study for how to optimize web performance and deliver the best experience to the user.</p>

<p>Let's dive in.</p>

<h2 id="what-is-a-modern-web-application">What is a Modern Web Application?</h2>

<p>Before we get to the tactical details of how to optimize our interaction with the network, it helps to understand the trends and the landscape of the problem we are up against. In other words, <em>what does a modern web page, or application look like?</em></p>

<p>The <a href="http://httparchive.org/"><span class="caps">HTTP</span> Archive</a> project tracks how the web is built, and it can help us answer this question. Instead of crawling the web for the content, it periodically crawls the most popular sites to record and aggregate analytics on the number of used resources, content types, headers, and other metadata for each individual destination. The stats, as of January 2013, may surprise you. An average page, amongst the top 300,000 destinations on the web is:</p>

<ul>
<li><strong>1280 <span class="caps">KB</span></strong> in size</li>
<li>composed of <strong>88 resources</strong></li>
<li>connects to <strong>15+ distinct hosts</strong></li>
</ul>

<p>Let that sink in. Over 1 <span class="caps">MB</span> in size on average, composed of 88 resources such as images, JavaScript, and <span class="caps">CSS</span>, and delivered from 15 different own and third-party hosts. Further, each of these numbers has been <a href="http://httparchive.org/trends.php">steadily increasing</a> over the past few years, and there are no signs of stopping. We are increasingly building larger and more ambitious web applications.</p>

<p>Applying basic math to the <span class="caps">HTTP</span> Archive numbers reveals that an average resource is about 15 <span class="caps">KB</span> in size (1280 <span class="caps">KB</span> / 88 resources), which means that most network transfers in the browser are short and bursty. This presents its own set of complications because the underlying transport (<span class="caps">TCP</span>) is optimized for large, streaming downloads. Let's peel back the onion and inspect one of these network requests.</p>

<h2 id="the-life-of-a-resource-request-on-the-wire">The Life of a Resource Request on the Wire</h2>

<p>The <span class="caps">W3C</span> <a href="http://www.w3.org/TR/navigation-timing/">Navigation Timing specification</a> provides a browser <span class="caps">API</span> and visibility into the timing and performance data behind the life of every request in the browser. Let's inspect the components, as each is a critical piece of delivering the optimal user experience:</p>

<div class="center figure">
<a name="figure-1.1"></a><img src="chrome-images/navtiming.png" alt="Figure 1.1 - Navigation Timing" title="Figure 1.1 - Navigation Timing" />
</div>

<p class="center figcaption">
<small>Figure 1.1 - Navigation Timing</small>
</p>

<p>Given the <span class="caps">URL</span> of a resource on the web, the browser starts by checking its local and application caches. If you have previously fetched the resource and the <a href="https://developers.google.com/speed/docs/best-practices/caching">appropriate cache headers</a> were provided (<code>Expires</code>, <code>Cache-Control</code>, etc.), then it is possible that we are allowed to use the local copy to fulfill the request–the fastest request is a request not made. Alternatively, if we have to revalidate the resource, if it expired, or if we simply have not seen it before, then a costly network request must be dispatched.</p>

<p>Given a hostname and resource path, Chrome first checks for existing open connections it is allowed to reuse–sockets are pooled by <code>{scheme, host, port}</code>. Alternatively, if you have configured a proxy, or specified a <a href="http://en.wikipedia.org/wiki/Proxy_auto-config">proxy auto-config</a> (<span class="caps">PAC</span>) script, then Chrome checks for connections through the appropriate proxy. <span class="caps">PAC</span> scripts allow for different proxies based on <span class="caps">URL</span>, or other specified rules, each of which can have its own socket pool. Finally, if neither of the above conditions is matched, then the request must begin by resolving the hostname to its <span class="caps">IP</span> address–also known as a <span class="caps">DNS</span> lookup.</p>

<p>If we are lucky, the hostname will already be cached in which case the response is usually just one quick system call away. If not, then a <span class="caps">DNS</span> query must be dispatched before any other work can happen. The time taken to do the <span class="caps">DNS</span> lookup will vary based on your internet provider, the popularity of the site and the likelihood of the hostname to be in intermediate caches, as well as the response time of the authoritative servers for that domain. In other words, there are a lot of variables at play, but it is not unusual for a <span class="caps">DNS</span> lookup to take up to several hundred milliseconds. Ouch.</p>

<div class="center figure">
<a name="figure-1.2"></a><img src="chrome-images/three-way.png" alt="Figure 1.2 - Three-way handshake" title="Figure 1.2 - Three-way handshake" />
</div>

<p class="center figcaption">
<small>Figure 1.2 - Three-way handshake</small>
</p>

<p>With the resolved <span class="caps">IP</span> address in hand, Chrome can now open a new <span class="caps">TCP</span> connection to the destination, which means that we must perform the "three-way handshake": <code>SYN &gt; SYN-ACK &gt; ACK</code>. This exchange adds a full round-trip of latency delay to each and every new <span class="caps">TCP</span> connection–no shortcuts. Depending on the distance between the client and the server, as well as the chosen routing path, this can yield from tens to hundreds, or even thousands, of milliseconds of delay. All of this work and latency is before even a single byte of application data has hit the wire.</p>

<p>Once the <span class="caps">TCP</span> handshake is complete, and if we are connecting to a secure destination (<span class="caps">HTTPS</span>), then the <span class="caps">SSL</span> handshake must take place. This can add up to two additional round-trips of latency delay between client and server. If the <span class="caps">SSL</span> session is cached, then we can "escape" with just one additional round-trip.</p>

<p>Finally, Chrome is able to dispatch the <span class="caps">HTTP</span> request (<code>requestStart</code> in <a href="#figure-1.1">Figure 1.1</a>). Once received, the server can process the request and then stream the response data back to the client. This incurs a minimum of one network round-trip, plus the processing time on the server. Following that, we are done–unless the actual response is an <span class="caps">HTTP</span> redirect, in which case we may have to repeat the entire cycle once over. Have a few gratuitous redirects on your pages? You may want to revisit that decision.</p>

<p>Have you been counting all the delays? To illustrate the problem, let's assume the worst case scenario for a typical broadband connection: local cache miss, followed by a relatively fast <span class="caps">DNS</span> lookup (50 ms), <span class="caps">TCP</span> handshake, <span class="caps">SSL</span> negotiation, and a relatively fast (100 ms) server response time, with a round-trip time (<span class="caps">RTT</span>) of 80 ms (an average round-trip across continental <span class="caps">USA</span>):</p>

<ul>
<li>50 ms for <span class="caps">DNS</span></li>
<li>80 ms for <span class="caps">TCP</span> handshake (one <span class="caps">RTT</span>)</li>
<li>160 ms for <span class="caps">SSL</span> handshake (two RTTs)</li>
<li>40 ms for request to server</li>
<li>100 ms for server processing</li>
<li>40 ms for response from the server</li>
</ul>

<p>That's 470 milliseconds for a single request, which translates to over 80% of network latency overhead as compared to the actual server processing time to fulfill the request–we have some work to do here. In fact, even 470 milliseconds may be an optimistic estimate:</p>

<ul>
<li>If the server response does not fit into the initial <span class="caps">TCP</span> congestion window (4-15 <span class="caps">KB</span>), then one or more additional round-trips of latency is introduced.<sup><a href="#fn1" class="footnoteRef" id="fnref1">1</a></sup></li>
<li><span class="caps">SSL</span> delays could get even worse if we need to fetch a missing certificate or perform an online certificate status check (<span class="caps">OCSP</span>), both of which will require an entirely new <span class="caps">TCP</span> connection, which can add hundreds and even thousands of milliseconds of additional latency.</li>
</ul>

<h2 id="what-is-fast-enough">What is "Fast Enough"?</h2>

<p>The network overhead of <span class="caps">DNS</span>, handshakes, and the round-trip times is what dominates the total time in our earlier case–the server response time accounts for only 20% of the total latency. But, in the grand scheme of things, do these delays even matter? If you are reading this, then you probably already know the answer: yes, very much so.</p>

<p>Past <a href="http://www.useit.com/papers/responsetime.html">user experience research</a> paints a consistent picture of what we, as users, expect in terms of responsiveness of any application, both offline and online:</p>

<table>
<caption><b>Table 1.1</b> - User perception of latency</caption>
<thead>
<tr class="header">
<th align="left">Delay</th>
<th align="left">User Reaction</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td align="left">0 - 100 ms</td>
<td align="left">Instant</td>
</tr>
<tr class="even">
<td align="left">100 - 300 ms</td>
<td align="left">Small perceptible delay</td>
</tr>
<tr class="odd">
<td align="left">300 - 1000 ms</td>
<td align="left">Machine is working</td>
</tr>
<tr class="even">
<td align="left">1 s+</td>
<td align="left">Mental context switch</td>
</tr>
<tr class="odd">
<td align="left">10 s+</td>
<td align="left">I'll come back later…</td>
</tr>
</tbody>
</table>

<p>Table 1.1 also explains the unofficial rule of thumb in the web performance community: render your pages, or at the very least, provide visual feedback in under 250 ms to keep the user engaged. This is not speed simply for speed's sake. Studies at Google, Amazon, Microsoft, as well as thousands of other sites show that additional latency has a direct impact on the bottom line of your site: faster sites yield more pageviews, higher engagement from the users, and see higher conversion rates.</p>

<p>So, there you have it, our optimal latency budget is 250 ms, and yet as we saw in the example above, the combination of a <span class="caps">DNS</span> lookup, the <span class="caps">TCP</span> and <span class="caps">SSL</span> handshakes, and propagation times for the request add up to 370 ms. We are 50% over budget, and we still have not factored in the server processing time!</p>

<p>To most users and even web developers, the <span class="caps">DNS</span>, <span class="caps">TCP</span>, and <span class="caps">SSL</span> delays are entirely transparent and are negotiated at network layers to which few of us descend or think about. However, each of these steps is critical to the overall user experience, since each extra network request can add tens or hundreds of milliseconds of latency. This is the reason why Chrome's network stack is much, much more than a simple socket handler.</p>

<p>Now that we have identified the problem, let's dive into the implementation details.</p>

<h2 id="chromes-network-stack-from-10000-feet">Chrome's Network Stack from 10,000 Feet</h2>

<h3 id="multi-process-architecture">Multi-process Architecture</h3>

<p>Chrome's multi-process architecture carries important implications for how each network request is handled within the browser. Under the hood, Chrome actually supports <a href="http://www.chromium.org/developers/design-documents/process-models">four different execution models</a> that determine how the process allocation is performed.</p>

<p>By default, desktop Chrome browsers use the process-per-site model, that isolates different sites from each other, but groups all instances of the same site into the same process. However, to keep things simple, let's assume one of the simplest cases: one distinct process for each open tab. From the network performance perspective, the differences here are not substantial, but the process-per-tab model is much easier to understand.</p>

<div class="center figure">
<a name="figure-1.3"></a><img src="chrome-images/multiproc.png" alt="Figure 1.3 - Multi-process architecture" title="Figure 1.3 - Multi-process architecture" />
</div>

<p class="center figcaption">
<small>Figure 1.3 - Multi-process architecture</small>
</p>

<p>The architecture dedicates one <em>render process</em> to each tab. Each render process contains instances of the Blink layout engine and the V8 JavaScript engine, along with glue code that bridges these (and a few other) components<sup><a href="#fn2" class="footnoteRef" id="fnref2">2</a></sup>.</p>

<p>Each of these render processes is executed within a sandboxed environment that has limited access to the user's computer–including the network. To gain access to these resources, each render process communicates with the main browser (or <em>kernel</em>) process, which is able to impose security and access policies on each renderer.</p>

<h3 id="inter-process-communication-and-multi-process-resource-loading">Inter-process Communication and Multi-process Resource Loading</h3>

<p>All communication between the renderer and the kernel process in Chrome is done via inter-process communication (<span class="caps">IPC</span>). On Linux and <span class="caps">OS</span> X, a <code>socketpair()</code> is used, which provides a named pipe transport for asynchronous communication. Each message from the renderer is serialized and passed to a dedicated I/O thread, which dispatches it to the main browser process. On the receiving end, the kernel process provides a filter interface, which allows Chrome to intercept resource <span class="caps">IPC</span> requests (see <a href="http://code.google.com/p/chromium/source/search?q=resourcemessagefilter&amp;origq=resourcemessagefilter&amp;btnG=Search+Trunk">ResourceMessageFilter</a>) which should be handled by the network stack.</p>

<div class="center figure">
<a name="figure-1.4"></a><img src="chrome-images/ipc.png" alt="Figure 1.4 - Inter-process communication" title="Figure 1.4 - Inter-process communication" />
</div>

<p class="center figcaption">
<small>Figure 1.4 - Inter-process communication</small>
</p>

<p>One of the advantages of this architecture is that all resource requests are handled entirely on the I/O threads and neither <span class="caps">UI</span>-generated activity nor network events interfere with each other. The resource filter runs in the I/O thread in the browser process, intercepts the resource request messages, and forwards them to a ResourceDispatcherHost<sup><a href="#fn3" class="footnoteRef" id="fnref3">3</a></sup> singleton in the browser process.</p>

<p>The singleton interface allows the browser to control each renderer's access to the network, but it also enables efficient and consistent resource sharing. Some examples include:</p>

<ul>
<li><strong>Socket pool and connection limits:</strong> the browser is able to enforce limits on the number of open sockets per profile (256), proxy (32), and <code>{scheme, host, port}</code> (6) groups. Note that this allows up to six <span class="caps">HTTP</span> and six <span class="caps">HTTPS</span> connections to the same <code>{host, port}</code>.</li>
<li><strong>Socket reuse:</strong> persistent <span class="caps">TCP</span> connections are retained in the socket pool for some time after servicing the request to enable connection reuse, which avoids the extra <span class="caps">DNS</span>, <span class="caps">TCP</span>, and <span class="caps">SSL</span> (if required) setup overhead imposed on each new connection.</li>
<li><strong>Socket late-binding:</strong> requests are associated with an underlying <span class="caps">TCP</span> connection only once the socket is ready to dispatch the application request, allowing better request prioritization (/e.g., arrival of a higher priority request while the socket was connecting), better throughput (e.g., re-use of a "warm" <span class="caps">TCP</span> connection in cases where an existing socket becomes available while a new connection is being opened), as well as a general-purpose mechanism for <span class="caps">TCP</span> pre-connect, and a number of other optimizations.</li>
<li><strong>Consistent session state:</strong> authentication, cookies, and cached data is shared between all render processes.</li>
<li><strong>Global resource and network optimizations:</strong> the browser is able to make decisions across all render processes and outstanding requests. For example, giving network priority to the requests initiated by the foreground tab.</li>
<li><strong>Predictive optimizations:</strong> by observing all network traffic, Chrome is able to build and refine predictive models to improve performance.</li>
</ul>

<p>As far as the render process is concerned, it is simply sending a resource request message over <span class="caps">IPC</span>, which is tagged with a unique request <span class="caps">ID</span> to the browser process, and the browser kernel process handles the rest.</p>

<h3 id="cross-platform-resource-fetching">Cross-platform Resource Fetching</h3>

<p>One of the chief concerns in the implementation of Chrome's network stack is portability across many different platforms: Linux, Windows, <span class="caps">OS</span> X, Chrome <span class="caps">OS</span>, Android, and iOS. To address this challenge, the network stack is implemented as a mostly single-threaded (there are separate cache and proxy threads) cross-platform library, which allows Chrome to reuse the same infrastructure and provide the same performance optimizations, as well as a greater opportunity for optimization across all platforms.</p>

<p>All of the network code is, of course, open source and can be found in the <a href="https://code.google.com/p/chromium/codesearch#chromium/src/net/&amp;ct=rc&amp;cd=1&amp;q=src.net&amp;sq=package:chromium"><code>src/net</code> subdirectory</a>. We will not examine each component in detail, but the layout of the code itself tells you a lot about its capabilities and structure. A few examples are listed in Table 1.2.</p>

<table>
<caption><b>Table 1.2</b> - Components of Chrome</caption>
<thead>
<tr class="header">
<th align="left">Component</th>
<th align="left">Description</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td align="left"><code>net/android</code></td>
<td align="left">Bindings to the Android runtime</td>
</tr>
<tr class="even">
<td align="left"><code>net/base</code></td>
<td align="left">Common net utilities, such as host resolution, cookies, network change detection, and <span class="caps">SSL</span> certificate management</td>
</tr>
<tr class="odd">
<td align="left"><code>net/cookies</code></td>
<td align="left">Implementation of storage, management, and retrieval of <span class="caps">HTTP</span> cookies</td>
</tr>
<tr class="even">
<td align="left"><code>net/disk_cache</code></td>
<td align="left">Disk and memory cache implementation for web resources</td>
</tr>
<tr class="odd">
<td align="left"><code>net/dns</code></td>
<td align="left">Implementation of an asynchronous <span class="caps">DNS</span> resolver</td>
</tr>
<tr class="even">
<td align="left"><code>net/http</code></td>
<td align="left"><span class="caps">HTTP</span> protocol implementation</td>
</tr>
<tr class="odd">
<td align="left"><code>net/proxy</code></td>
<td align="left">Proxy (<span class="caps">SOCKS</span> and <span class="caps">HTTP</span>) configuration, resolution, script fetching, etc.</td>
</tr>
<tr class="even">
<td align="left"><code>net/socket</code></td>
<td align="left">Cross-platform implementations of <span class="caps">TCP</span> sockets, <span class="caps">SSL</span> streams, and socket pools</td>
</tr>
<tr class="odd">
<td align="left"><code>net/spdy</code></td>
<td align="left"><span class="caps">SPDY</span> protocol implementation</td>
</tr>
<tr class="even">
<td align="left"><code>net/url_request</code></td>
<td align="left">URLRequest, URLRequestContext, and URLRequestJob implementations</td>
</tr>
<tr class="odd">
<td align="left"><code>net/websockets</code></td>
<td align="left">WebSockets protocol implementation</td>
</tr>
</tbody>
</table>

<p>The code for each of the components makes for a great read for the curious–it is well documented, and you will find plenty of unit tests for every component.</p>

<h3 id="architecture-and-performance-on-mobile-platforms">Architecture and Performance on Mobile Platforms</h3>

<p>Mobile browser usage is growing at an exponential rate and even by modest projections, it will eclipse desktop browsing in the not so distant future. Needless to say, delivering an optimized mobile experience has been a top priority for the Chrome team. In early 2012, <a href="http://www.google.com/intl/en/chrome/browser/mobile/android.html">Chrome for Android</a> was announced, and a few months later, <a href="http://www.google.com/intl/en/chrome/browser/mobile/ios.html">Chrome for iOS</a> followed.</p>

<p>The first thing to note about the mobile version of Chrome, is that it is not simply a direct adaptation of the desktop browser–that would not deliver the best user experience. By its very nature, the mobile environment is both much more resource constrained, and has many fundamentally different operating parameters:</p>

<ul>
<li>Desktop users navigate with the mouse, may have overlapping windows, have a large screen, are mostly not power constrained, usually have a much more stable network connection, and have access to much larger pools of storage and memory.</li>
<li>Mobile users use touch and gesture navigation, have a much smaller screen, are battery and power constrained, are often on metered connections, and have limited local storage and memory.</li>
</ul>

<p>Further, there is no such thing as a "typical mobile device". Instead there is a wide range of devices with varying hardware capabilities, and to deliver the best performance, Chrome must adapt to the operating constraints of each and every device. Thankfully, the various execution models allow Chrome to do exactly that.</p>

<p>On Android devices, Chrome leverages the same multi-process architecture as the desktop version–there is a browser process, and one or more renderer processes. The one difference is that due to memory constraints of the mobile device, Chrome may not be able to run a dedicated renderer for each open tab. Instead, Chrome determines the optimal number of renderer processes based on available memory, and other constraints of the device, and shares the renderer process between the multiple tabs.</p>

<p>In cases where only minimal resources are available, or if Chrome is unable to run multiple processes, it can also switch to use a single-process, multi-threaded processing model. In fact, on iOS devices, due to sandboxing restrictions of the underlying platform, it does exactly that–it runs a single, but multi-threaded process.</p>

<p>What about network performance? First off, Chrome uses the same network stack on Android and iOS as it does on all other versions. This enables all of the same network optimizations across all platforms, which gives Chrome a significant performance advantage. However, what is different, and is often adjusted based on the capabilities of the device and the network in use, are variables such as priority of speculative optimization techniques, socket timeouts and management logic, cache sizes, and more.</p>

<p>For example, to preserve battery, mobile Chrome may opt in to use lazy closing of idle sockets–sockets are closed only when opening new ones to minimize radio use. Similarly, since prerendering (which we will discuss below), may require significant network and processing resources, it is often only enabled when the user is on Wi-Fi.</p>

<p>Optimizing the mobile browsing experience is one of the highest priority items for the Chrome development team, and we can expect to see a lot of new improvements in the months and years to come. In fact, it is a topic that deserves its own separate chapter–perhaps in the next installment of the <span class="caps">POSA</span> series.</p>

<h3 id="speculative-optimization-with-chromes-predictor">Speculative Optimization with Chrome's Predictor</h3>

<p>Chrome gets faster as you use it. This feat is accomplished with the help of a singleton <code>Predictor</code> object, which is instantiated within the browser kernel process, and whose sole responsibility is to observe network patterns and to learn and anticipate likely user actions in the future. A few example signals processed by the <code>Predictor</code> include:</p>

<ul>
<li>Users hovering their mouse over a link is a good indicator of a likely, upcoming navigation event, which Chrome can help accelerate by dispatching a speculative <span class="caps">DNS</span> lookup of the target hostname, as well as potentially starting the <span class="caps">TCP</span> handshake. By the time the user clicks, which takes ~200 ms on average, there is a good chance that we have already completed the <span class="caps">DNS</span> and <span class="caps">TCP</span> steps, allowing us to eliminate hundreds of milliseconds of extra latency for the navigation event.</li>
<li>Typing in the Omnibox (<span class="caps">URL</span>) bar triggers high-likelihood suggestions, which may similarly kick off a <span class="caps">DNS</span> lookup, <span class="caps">TCP</span> pre-connect, and even prerender the page in a hidden tab.</li>
<li>Each one of us has a list of favorite sites that we visit every day. Chrome can learn the subresources on these sites and speculatively pre-resolve and perhaps even prefetch them to accelerate the browsing experience.</li>
</ul>

<p>Chrome learns the topology of the web, as well as your own browsing patterns, as you use it. If it does the job well, it can eliminate hundreds of milliseconds of latency from each navigation and get the user closer to the Holy Grail of the "instant page load". To achieve this goal, Chrome leverages four core optimization techniques listed in Table 1.3.</p>

<table>
<caption><b>Table 1.3</b> - Network optimization techniques used by Chrome</caption>
<thead>
<tr class="header">
<th align="left">Technique</th>
<th align="left">Description</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td align="left"><span class="caps">DNS</span> pre-resolve</td>
<td align="left">Resolve hostnames ahead of time, to avoid <span class="caps">DNS</span> latency</td>
</tr>
<tr class="even">
<td align="left"><span class="caps">TCP</span> pre-connect</td>
<td align="left">Connect to destination server ahead of time, to avoid <span class="caps">TCP</span> handshake latency</td>
</tr>
<tr class="odd">
<td align="left">Resource prefetching</td>
<td align="left">Fetch critical resources on the page ahead of time, to accelerate rendering of the page</td>
</tr>
<tr class="even">
<td align="left">Page prerendering</td>
<td align="left">Fetch the entire page with all of its resources ahead of time, to enable instant navigation when triggered by the user</td>
</tr>
</tbody>
</table>

<p>Each decision to invoke one or several of these techniques is optimized against a large number of constraints. After all, each is a speculative optimization, which means that if done poorly, it might trigger unnecessary work and network traffic, or even worse, have a negative effect on the loading time for an actual navigation triggered by the user.</p>

<p>How does Chrome address this problem? The predictor consumes as many signals as it can, which include user generated actions, historical browsing data, as well as signals from the renderer and the network stack itself.</p>

<p>Not unlike the <code>ResourceDispatcherHost</code>, which is responsible for coordinating all of the network activity within Chrome, the <code>Predictor</code> object creates a number of filters on user and network generated activity within Chrome:</p>

<ul>
<li><span class="caps">IPC</span> channel filter to monitor for signals from the render processes</li>
<li><code>ConnectInterceptor</code> object is added to each request, such that it can observe the traffic patterns and record success metrics for each request</li>
</ul>

<p>As a hands-on example, the render process can trigger a message to the browser process with any of the following hints, which are conveniently defined in <code>ResolutionMotivation</code> (<a href="http://code.google.com/searchframe#OAMlx_jo-ck/src/chrome/browser/net/url_info.h&amp;l=35"><code>url_info.h</code></a>):</p>

<pre class="sourceCode C"><code class="sourceCode c"><span class="kw">enum</span> ResolutionMotivation {
  MOUSE_OVER_MOTIVATED,     <span class="co">// Mouse-over initiated by the user.</span>
  OMNIBOX_MOTIVATED,        <span class="co">// Omnibox suggested resolving this.</span>
  STARTUP_LIST_MOTIVATED,   <span class="co">// This resource is on the top 10 startup list.</span>
  EARLY_LOAD_MOTIVATED,     <span class="co">// In some cases we use the prefetcher to warm up</span>
                            <span class="co">// the connection in advance of issuing the real</span>
                            <span class="co">// request.</span>

  <span class="co">// The following involve predictive prefetching, triggered by a navigation.</span>
  <span class="co">// The referring_url_ is also set when these are used.</span>
  STATIC_REFERAL_MOTIVATED,  <span class="co">// External database suggested this resolution.</span>
  LEARNED_REFERAL_MOTIVATED, <span class="co">// Prior navigation taught us this resolution.</span>
  SELF_REFERAL_MOTIVATED,    <span class="co">// Guess about need for a second connection.</span>

  <span class="co">// &lt;snip&gt; ...</span>
};</code></pre>

<p>Given such a signal, the goal of the predictor is to evaluate the likelihood of its success, and then to trigger the activity if resources are available. Every hint may have a likelihood of success, a priority, and an expiration timestamp, the combination of which can be used to maintain an internal priority queue of speculative optimizations. Finally, for every dispatched request from within this queue, the predictor is also able to track its success rate, which allows it to further optimize its future decisions.</p>

<h3 id="chrome-network-architecture-in-a-nutshell">Chrome Network Architecture in a Nutshell</h3>

<ul>
<li>Chrome uses a multi-process architecture, which isolates render processes from the browser process.</li>
<li>Chrome maintains a single instance of the resource dispatcher, which is shared across all render processes, and runs within the browser kernel process.</li>
<li>The network stack is a cross-platform, mostly single-threaded library.</li>
<li>The network stack uses non-blocking operations to manage all network operations.</li>
<li>Shared network stack allows efficient resource prioritization, reuse, and provides the browser with ability to perform global optimization across all running processes.</li>
<li>Each render process communicates with the resource dispatcher via <span class="caps">IPC</span>.</li>
<li>Resource dispatcher intercepts resource requests via a custom <span class="caps">IPC</span> filter.</li>
<li>Predictor intercepts resources request and response traffic to learn and optimize future network requests.</li>
<li>Predictor may speculatively schedule <span class="caps">DNS</span>, <span class="caps">TCP</span>, and even resource requests based on learned traffic patterns, saving hundreds of milliseconds when the navigation is triggered by the user.</li>
</ul>

<h2 id="the-lifetime-of-your-browser-session">The Lifetime of Your Browser Session</h2>

<p>With the 10,000 foot architecture view of the Chrome network stack in mind, let's now take a closer look at the kinds of user-facing optimizations enabled within the browser. Specifically, let's imagine we have just created a new Chrome profile and are ready to start our day.</p>

<h3 id="optimizing-the-cold-boot-experience">Optimizing the Cold-boot Experience</h3>

<p>The first time you load your browser, it knows little about your favorite sites or navigation patterns. However, many of us follow the same routine after a cold boot of the browser, where we may navigate to our email inbox, favorite news site, a social site, an internal portal, and so on. The specific sites will vary, but the similarity of these sessions allows the Chrome Predictor to accelerate your cold-boot experience.</p>

<p>Chrome remembers the top ten most likely hostnames accessed by the user following the browser start–note that this is not the top ten global destinations, but specifically the destinations following a fresh browser start. As the browser loads, Chrome can trigger a <span class="caps">DNS</span> prefetch for the likely destinations. If you are curious, you can inspect your own startup hostname list by opening a new tab and navigating to <code>chrome://dns</code>. At the top of the page, you will find the list of the top ten most likely startup candidates for your profile.</p>

<div class="center figure">
<a name="figure-1.5"></a><img src="chrome-images/startup-dns.png" alt="Figure 1.5 - Startup DNS" title="Figure 1.5 - Startup DNS" />
</div>

<p class="center figcaption">
<small>Figure 1.5 - Startup <span class="caps">DNS</span></small>
</p>

<p>The screenshot in <a href="#figure-1.5">Figure 1.5</a> is an example from my own Chrome profile. How do I usually begin my browsing? Frequently by navigating to Google Docs if I'm working on an article such as this one. Not surprisingly, we see a lot of Google hostnames in the list.</p>

<h3 id="optimizing-interactions-with-the-omnibox">Optimizing Interactions with the Omnibox</h3>

<p>One of the innovations of Chrome was the introduction of the Omnibox, which unlike its predecessors handles much more than just destination URLs. Besides remembering the URLs of pages that the user visited in the past, it also offers full text search over your history, as well as a tight integration with the search engine of your choice.</p>

<p>As the user types, the Omnibox automatically proposes an action, which is either a <span class="caps">URL</span> based on your navigation history, or a search query. Under the hood, each proposed action is scored with respect to the query, as well as its past performance. In fact, Chrome allows us to inspect this data by visiting <code>chrome://predictors</code>.</p>

<div class="center figure">
<a name="figure-1.6"></a><img src="chrome-images/omni-pagespeed.png" alt="Figure 1.6 - Omnibox URL prediction" title="Figure 1.6 - Omnibox URL prediction" />
</div>

<p class="center figcaption">
<small>Figure 1.6 - Omnibox <span class="caps">URL</span> prediction</small>
</p>

<p>Chrome maintains a history of the user-entered prefixes, the actions it has proposed, as well as the hit rate for each one. For my own profile, you can see that whenever I enter "g" in the Omnibox, there is a 76% chance that I'm heading to Gmail. Once I add an "m" (for "gm"), then the confidence rises to 99.8%–in fact, out of the 412 recorded visits, I did not end up going to Gmail after entering "gm" only once.</p>

<!-- TODO Change this to not refer to specific colours. -->

<p>What does this have to do with the network stack? The yellow and green colors for the likely candidates are also important signals for the <code>ResourceDispatcher</code>. If we have a likely candidate (yellow), Chrome may trigger a <span class="caps">DNS</span> prefetch for the target host. If we have a high confidence candidate (green), then Chrome may also trigger a <span class="caps">TCP</span> pre-connect once the hostname has been resolved. Finally, if both complete while the user is still deliberating, then Chrome may even prerender the entire page in a hidden tab.</p>

<p>Alternatively, if there is no good match for the entered prefix based on past navigation history then Chrome may issue a <span class="caps">DNS</span> prefetch and <span class="caps">TCP</span> pre-connect to your search provider in anticipation of a likely search request.</p>

<p>An average user takes hundreds of milliseconds to fill in their query and to evaluate the proposed autocomplete suggestions. In the background, Chrome is able to prefetch, pre-connect, and in certain cases even prerender the page, so that by the time the user is ready to hit the "enter" key, much of the network latency has already been eliminated.</p>

<h3 id="optimizing-cache-performance">Optimizing Cache Performance</h3>

<p>The best, and the fastest request, is a request not made. Whenever we talk about performance, we would be amiss if we did not talk about the cache–you are providing <code>Expires</code>, <code>ETag</code>, <code>Last-Modified</code>, and <code>Cache-Control</code> <a href="https://developers.google.com/speed/docs/best-practices/caching">response headers</a> for all the resources on your web pages, right? If not, go fix it. We'll wait.</p>

<p>Chrome has two different implementations of the internal cache: one backed by local disk, and second which stores everything in memory. The in-memory implementation is used for the <a href="http://support.google.com/chrome/bin/answer.py?hl=en&amp;answer=95464">Incognito browsing mode</a> and is wiped clean whenever you close the window. Both implement the same internal interface (<code>disk_cache::Backend</code>, and <code>disk_cache::Entry</code>), which greatly simplifies the architecture and–if you are so inclined–allows you to easily experiment with your own experimental cache implementations.</p>

<p>Internally the disk cache implements its own set of data structures, all of which are stored within a single cache folder for your profile. Inside this folder there are index files, which are memory-mapped when the browser starts, and data files, which store the actual data alongside the <span class="caps">HTTP</span> headers and other bookkeeping information.<sup><a href="#fn4" class="footnoteRef" id="fnref4">4</a></sup> Finally, for eviction, the disk cache maintains a Least Recently Used (<span class="caps">LRU</span>) cache that takes into account ranking metrics such as frequency of access and resource age.</p>

<p></p>

<p>If you are ever curious about the state of the Chrome cache you can open a new tab and navigate to <code>chrome://net-internals/#httpCache</code>. Alternatively, if you want to see the actual <span class="caps">HTTP</span> metadata and the cached response, you can also visit <code>chrome://cache</code>, which will enumerate all of the resources currently available in the cache. From that page, search for a resource you are looking for and click on the <span class="caps">URL</span> to see the exact, cached headers and response bytes.</p>

<h3 id="optimizing-dns-with-prefetching">Optimizing <span class="caps">DNS</span> with Prefetching</h3>

<p>We have already mentioned <span class="caps">DNS</span> pre-resolution on several occasions, so before we dive into the implementation, let's review the cases in which it may be triggered, and why:</p>

<ul>
<li>The Blink document parser, which runs in the render process, may provide a list of hostnames for all the links on the current page, which Chrome may choose to resolve ahead of time.</li>
<li>The render process may trigger a mouse hover or "button down" event as an early signal of a user's intent to perform a navigation.</li>
<li>The Omnibox may trigger a resolve request based on a high likelihood suggestion.</li>
<li>The Predictor may request hostname resolution based on past navigation and resource request data.</li>
<li>The owner of the page may explicitly indicate to Chrome which hostnames it should pre-resolve.</li>
</ul>

<p>In all cases, <span class="caps">DNS</span> pre-resolution is treated as a hint. Chrome does not guarantee that the pre-resolution will occur, rather it uses each signal in combination with its own predictor to assess the hint and decide on a course of action. In the "worst case", if Chrome was not able to pre-resolve the hostname in time, the user would have to wait for an explicit <span class="caps">DNS</span> resolution followed by <span class="caps">TCP</span> connection time, and finally the actual resource fetch. However, when this occurs, the predictor can take note and adjust its future decisions accordingly–it gets faster, and smarter, as you use it.</p>

<p>One of the optimizations we have not covered previously is the ability of Chrome to learn the topology of each site and then use this information to accelerate future visits. Specifically, recall that an average page consists of 88 resources, which are delivered from 15+ distinct hosts. Each time you perform a navigation, Chrome may record the hostnames for the popular resources on the page, and during a future visit, it may choose to trigger a <span class="caps">DNS</span> pre-resolve and even a <span class="caps">TCP</span> pre-connect for some or all of them.</p>

<p></p>

<p>To inspect the subresource hostnames stored by Chrome, navigate to <code>chrome://dns</code> and search for any popular destination hostname for your profile. In the example above, you can see the six subresource hostnames that Chrome remembered for Google+, as well as stats for the number of cases when a <span class="caps">DNS</span> pre-resolution was triggered, or a <span class="caps">TCP</span> pre-connect was performed, as well as an expected number of requests that will be served by each. This internal accounting is what enables the Chrome predictor to perform its optimizations.</p>

<p>In addition to all of the internal signals, the owner of the site is also able to embed additional markup on their pages to request the browser to pre-resolve a hostname:</p>

<pre><code>  &lt;link rel=&quot;dns-prefetch&quot; href=&quot;//host_name_to_prefetch.com&quot;&gt;</code></pre>

<p>Why not simply rely on the automated machinery in the browser? In some cases, you may want to pre-resolve a hostname which is not mentioned anywhere on the page. A redirect is the canonical example: a link may point to a host–like an analytics tracking service–which then redirects the user to the actual destination. By itself, Chrome cannot infer this pattern, but you can help it by providing a manual hint and get the browser to resolve the hostname of the actual destination ahead of time.</p>

<p>How is this all implemented under the hood? The answer to this question, just like all other optimizations we have covered, depends on the version of Chrome, since the team is always experimenting with new and better ways to improve performance. However, broadly speaking, the <span class="caps">DNS</span> infrastructure within Chrome has two major implementations. Historically, Chrome has relied on the platform-independent <code>getaddrinfo()</code> system call, and delegated the actual responsibility for the lookups to the operating system. However, this approach is in the process of being replaced with Chrome's own implementation of an asynchronous <span class="caps">DNS</span> resolver.</p>

<p>The original implementation, which relied on the operating system, has its benefits: less and simpler code, and the ability to leverage the operating system's <span class="caps">DNS</span> cache. However, <code>getaddrinfo()</code> is also a blocking system call, which meant that Chrome had to create and maintain a dedicated worker thread-pool to allow it to perform multiple lookups in parallel. This unjoined pool was capped at six worker threads, which is an empirical number based on lowest common denominator of hardware–turns out, higher numbers of parallel requests can overload some users' routers.</p>

<p>For pre-resolution with the worker-pool, Chrome simply dispatches the <code>getaddrinfo()</code> call, which blocks the worker thread until the response is ready, at which point it just discards the returned result and begins processing the next prefetch request. The result is cached by the <span class="caps">OS</span> <span class="caps">DNS</span> cache, which returns an immediate response to future, actual <code>getaddrinfo()</code> lookups. It's simple, effective, and works well enough in practice.</p>

<p>Well, effective, but not good enough. The <code>getaddrinfo()</code> call hides a lot of useful information from Chrome, such as the time-to-live (<span class="caps">TTL</span>) timestamps for each record, as well as the state of the <span class="caps">DNS</span> cache itself. To improve performance, the Chrome team decided to implement their own, cross-platform, asynchronous <span class="caps">DNS</span> resolver.</p>

<div class="center figure">
<a name="figure-1.7"></a><img src="chrome-images/async-dns.jpg" alt="Figure 1.7 - Enabling the asynchronous DNS resolver" title="Figure 1.7 - Enabling the asynchronous DNS resolver" />
</div>

<p class="center figcaption">
<small>Figure 1.7 - Enabling the asynchronous <span class="caps">DNS</span> resolver</small>
</p>

<p>By moving <span class="caps">DNS</span> resolution into Chrome, the new async resolver enables a number of new optimizations:</p>

<ul>
<li>better control of retransmission timers, and ability to execute multiple queries in parallel</li>
<li>visibility into record TTLs, which allows Chrome to refresh popular records ahead of time</li>
<li>better behavior for dual stack implementations (IPv4 and IPv6)</li>
<li>failovers to different servers, based on <span class="caps">RTT</span> or other signals</li>
</ul>

<p>All of the above, and more, are ideas for continuous experimentation and improvement within Chrome. Which brings us to the obvious question: how do we know and measure the impact of these ideas? Simple, Chrome tracks detailed network performance stats and histograms for each individual profile. To inspect the collected <span class="caps">DNS</span> metrics, open a new tab, and head to <code>chrome://histograms/DNS</code> (see <a href="#figure-1.8">Figure 1.8</a>).</p>

<div class="center figure">
<a name="figure-1.8"></a><img src="chrome-images/dns-prefetch.png" alt="Figure 1.8 - DNS prefetch histograms" title="Figure 1.8 - DNS prefetch histograms" />
</div>

<p class="center figcaption">
<small>Figure 1.8 - <span class="caps">DNS</span> prefetch histograms</small>
</p>

<p>The above histogram shows the distribution of latencies for <span class="caps">DNS</span> prefetch requests: roughly 50% (rightmost column) of the prefetch queries were finished within 20 ms (leftmost column). Note that this is data based on a recent browsing session (9869 samples), and is private to the user. If the user has opted in to report their usage stats in Chrome, then the summary of this data is anonymized and periodically beaconed back to the engineering team, which is then able to see the impact of their experiments and adjust accordingly.</p>

<h3 id="optimizing-tcp-connection-management-with-pre-connect">Optimizing <span class="caps">TCP</span> Connection Management with Pre-connect</h3>

<p>We have pre-resolved the hostname and we have a high likelihood navigation event that is about to happen, as estimated by the Omnibox, or the Chrome predictor. Why not go one step further, and also speculatively pre-connect to the destination host and complete the <span class="caps">TCP</span> handshake before the user dispatches the request? By doing so, we can eliminate another full round-trip of latency delay, which can easily save hundreds of milliseconds for the user. Well, that is exactly what <span class="caps">TCP</span> pre-connect is and how it works.</p>

<!-- TODO wat is pre-connect? are we hyphenating the same way in the mobile-perf chapter, check hyphenation for all pre- prefixed words -->

<p>To see the hosts for which a <span class="caps">TCP</span> pre-connect has been triggered, open a new tab and visit <code>chrome://dns</code>.</p>

<div class="center figure">
<a name="figure-1.9"></a><img src="chrome-images/preconn-pagespeed.png" alt="Figure 1.9 - Showing hosts for which TCP pre-connects have been triggered" title="Figure 1.9 - Showing hosts for which TCP pre-connects have been triggered" />
</div>

<p class="center figcaption">
<small>Figure 1.9 - Showing hosts for which <span class="caps">TCP</span> pre-connects have been triggered</small>
</p>

<p>First, Chrome checks its socket pools to see if there is an available socket for the hostname, which it may be able to reuse–keep-alive sockets are kept in the pool for some period of time, to avoid the <span class="caps">TCP</span> handshake and slow-start penalties. If no socket is available, then it can initiate the <span class="caps">TCP</span> handshake, and place it in the pool. Then, when the user initiates the navigation, the <span class="caps">HTTP</span> request can be dispatched immediately.</p>

<p>For the curious, Chrome provides a utility at <code>chrome://net-internals#sockets</code> for exploring the state of all the open sockets in Chrome. A screenshot is shown in <a href="#figure-1.10">Figure 1.10</a>.</p>

<div class="center figure">
<a name="figure-1.10"></a><img src="chrome-images/netinternals.png" alt="Figure 1.10 - Open sockets" title="Figure 1.10 - Open sockets" />
</div>

<p class="center figcaption">
<small>Figure 1.10 - Open sockets</small>
</p>

<p>Note that you can also drill into each socket and inspect the timeline: connect and proxy times, arrival times for each packet, and more. Last but not least, you can also export this data for further analysis or a bug report. Having good instrumentation is key to any performance optimization, and <code>chrome://net-internals</code> is the nexus of all things networking in Chrome–if you have not explored it yet, you should!</p>

<h3 id="optimizing-resource-loading-with-prefetch-hints">Optimizing Resource Loading with Prefetch Hints</h3>

<p>Sometimes the author of a page is able to provide additional navigation, or page context, based on the structure or the layout of their site, and help the browser optimize the experience for the user. Chrome supports two such hints, which can be embedded in the markup of the page:</p>

<pre><code>  &lt;link rel=&quot;subresource&quot; href=&quot;/javascript/myapp.js&quot;&gt;
  &lt;link rel=&quot;prefetch&quot;    href=&quot;/images/big.jpeg&quot;&gt;</code></pre>

<p>Subresource and prefetch look very similar, but have very different semantics. When a link resource specifies its relationship as "prefetch", it is an indication to the browser that this resource might be needed in a future navigation. In other words, it is effectively a cross-page hint. By contrast, when a resource specifies the relationship as a "subresource", it is an early indication to the browser that the resource will be used on a current page, and that it may want to dispatch the request before it encounters it later in the document.</p>

<p>As you would expect, the different semantics of the hints lead to very different behavior by the resource loader. Resources marked with prefetch are considered low priority and might be downloaded by the browser only once the current page has finished loading. Subresource resources are fetched with high priority as soon as they are encountered and will compete with the rest of the resources on the current page.</p>

<p>Both hints, when used well and in the right context, can help significantly with optimizing the user experience on your site. Finally, it is also important to note that prefetch is <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#link-type-prefetch">part of the <span class="caps">HTML5</span> spec</a>, and as of today supported by Firefox and Chrome, whereas subresource is currently <a href="http://www.chromium.org/spdy/link-headers-and-server-hint/link-rel-subresource">only available in Chrome</a>.</p>

<h3 id="optimizing-resource-loading-with-browser-prefreshing">Optimizing Resource Loading with Browser Prefreshing</h3>

<p>Unfortunately, not all site owners are able or willing to provide the browser with subresource hints in their markup. Further, even if they do, we must wait for the <span class="caps">HTML</span> document to arrive from the server before we are able to parse the hints and begin fetching the necessary subresources–depending on the server response time, as well as the latency between the client and the server, this could take hundreds and even thousands of milliseconds.</p>

<p>However, as we saw earlier, Chrome is already learning the hostnames of the popular resources to perform <span class="caps">DNS</span> prefetching. So, why couldn't it do the same, but go one step further and perform the <span class="caps">DNS</span> lookup, use <span class="caps">TCP</span> pre-connect, and then also speculatively prefetch the resource? Well, that is exactly what <em>prefreshing</em> could do:</p>

<ul>
<li>User initiates a request to a target <span class="caps">URL</span></li>
<li>Chrome queries its predictor for learned subresources associated with the target <span class="caps">URL</span> and initiates the sequence of <span class="caps">DNS</span> prefetch, <span class="caps">TCP</span> pre-connect, and resource prefreshing</li>
<li>If the learned subresource is in the cache, then it is loaded from disk and into memory</li>
<li>If the learned subresource is missing, or has expired, then a network request is made</li>
</ul>

<p>Resource prefreshing is a great example of the workflow of every experimental optimization in Chrome–in theory, it should enable better performance, but there are many tradeoffs as well. There is only one way to reliably determine if it will make the cut and make it into Chrome: implement it and run it as an A/B experiment in some of the pre-release channels with real users, on real networks, with real browsing patterns.</p>

<p>As of early 2013, the Chrome team is in the early stages of discussing the implementation. If it makes the cut based on gathered results, we may see prefreshing in Chrome sometime later in the year. The process of improving Chrome network performance never stops–the team is always experimenting with new approaches, ideas, and techniques.</p>

<h3 id="optimizing-navigation-with-prerendering">Optimizing Navigation with Prerendering</h3>

<p>Each and every optimization we have covered up to now helps reduce the latency between the user's direct request for a navigation and the resulting page rendering in their tab. However, what would it take to have a truly instant experience? Based on the <span class="caps">UX</span> data we saw earlier, this interaction would have to happen in less than 100 ms, which does not leave much room for network latency at all. What could we do to deliver a rendered page in less than 100 ms?</p>

<p>Of course, you already know the answer, since this is a common pattern employed by many users: if you open multiple tabs then switching between tabs is instant and is definitely much faster than waiting for the navigation between the same resources in a single foreground tab. Well, what if the browser provided an <span class="caps">API</span> to do this?</p>

<pre><code>  &lt;link rel=&quot;prerender&quot; href=&quot;http://example.org/index.html&quot;&gt;</code></pre>

<p>You guessed it, that is <a href="https://developers.google.com/chrome/whitepapers/prerender">prerendering in Chrome</a>. Instead of just downloading a single resource, as the "prefetch" hint would have done, the "prerender" attribute indicates to Chrome that it should, well, prerender the page in a hidden tab, along with all of its subresources. The hidden tab itself is invisible to the user, but when the user triggers the navigation, the tab is swapped in from the background for an "instant experience".</p>

<p>Curious to try it out? You can visit <a href="http://prerender-test.appspot.com">prerender-test.appspot.com</a> for a hands-on demo, and see the history and status of the prerendered pages for your profile by visiting: <code>chrome://net-internals/#prerender</code>. (See <a href="#figure-1.11">Figure 1.11</a>.)</p>

<div class="center figure">
<a name="figure-1.11"></a><img src="chrome-images/netinternals-prerender.png" alt="Figure 1.11 - Prerendered pages for the current profile" title="Figure 1.11 - Prerendered pages for the current profile" />
</div>

<p class="center figcaption">
<small>Figure 1.11 - Prerendered pages for the current profile</small>
</p>

<p>As you would expect, rendering an entire page in a hidden tab can consume a lot of resources, both <span class="caps">CPU</span> and network, and hence should only be used in cases where we have high confidence that the hidden tab will be used. For example, when you are using the Omnibox, a prerender may be triggered for a high confidence suggestion. Similarly, Google Search sometimes adds the prerender hint to its markup if it estimates that the first search result is a high confidence destination (also known as Google Instant Pages):</p>

<p>Note that you can also add prerender hints to your own site. Before you do, note that prerendering has a number of restrictions and limitations, which you should keep in mind:</p>

<ul>
<li>At most one prerender tab is allowed across all processes</li>
<li><span class="caps">HTTPS</span> and pages with <span class="caps">HTTP</span> authentication are not allowed</li>
<li>Prerendering is abandoned if the requested resource, or any of its subresources need to make a non-idempotent request (only <span class="caps">GET</span> requests allowed)</li>
<li>All resources are fetched with lowest network priority</li>
<li>The page is rendered with lowest <span class="caps">CPU</span> priority</li>
<li>The page is abandoned if memory requirements exceed 100 <span class="caps">MB</span></li>
<li>Plugin initialization is deferred, and prerendering is abandoned if an <span class="caps">HTML5</span> media element is present</li>
</ul>

<p>In other words, prerendering is not guaranteed to happen and only applies to pages where it is safe. Additionally, since JavaScript and other logic may be executed within the hidden page, it is best practice to leverage the <a href="https://developers.google.com/chrome/whitepapers/pagevisibility">Page Visibility <span class="caps">API</span></a> to detect if the page is visible–which is something you <a href="http://www.html5rocks.com/en/tutorials/pagevisibility/intro/">should be doing anyway</a>.</p>

<h2 id="chrome-gets-faster-as-you-use-it">Chrome Gets Faster as You Use It</h2>

<p>Needless to say, Chrome's network stack is much more than a simple socket manager. Our whirlwind tour covered the many levels of potential optimizations that are performed transparently in the background, as you navigate the web. The more Chrome learns about the topology of the web and your browsing patterns, the better it can do its job. Almost like magic, Chrome gets faster as you use it. Except, it is not magic, because now you know how it works.</p>

<p>Finally, it is important to note that the Chrome team continues to iterate and experiment with new ideas to improve performance–this process never stops. By the time you read this, chances are there will be new experiments and optimizations being developed, tested, or deployed. Perhaps once we reach our target destination of instant page loads (&lt; 100 ms), for each and every page, then we can take a break. Until then, there is always more work to do.</p>

<div class="footnotes">
<ol>
<li id="fn1"><p><em>Chapter 10: Secrets of Mobile Network Performance</em> covers this issue in greater detail.<a href="#fnref1">↩</a></p></li>
<li id="fn2"><p>If you are curious, the Chromium wiki contains a <a href="http://www.chromium.org/developers/design-documents/multi-process-architecture">great introduction to the plumbing</a>.<a href="#fnref2">↩</a></p></li>
<li id="fn3"><p><a href="http://code.google.com/searchframe#OAMlx_jo-ck/src/content/public/browser/resource_dispatcher_host.h&amp;exact_package=chromium&amp;q=ResourceDispatcherHost">Http://code.google.com/searchframe#OAMlx_jo-ck/src/content/public/browser/resource_dispatcher_host.h&amp;exact_package=chromium&amp;q=ResourceDispatcherHost</a>.<a href="#fnref3">↩</a></p></li>
<li id="fn4"><p>Resources up to 16 <span class="caps">KB</span> in size are stored in shared data block-files, and larger files get their own dedicated files on disk.<a href="#fnref4">↩</a></p></li>
</ol>
</div>
  </body>
</html>
